package org.peterbjornx.net.packet;

import org.peterbjornx.io.RSInputStream;
import org.peterbjornx.io.BitStream;
import org.peterbjornx.Client;
import org.peterbjornx.config.ProtocolSettings;
import static org.peterbjornx.config.ProtocolSettings.MAP_OFF;
import org.peterbjornx.util.TextUtils;
import org.peterbjornx.model.Player;

import java.io.IOException;
import java.io.ByteArrayInputStream;

/**
 * Created by IntelliJ IDEA.
 * User: PeterbjornX
 * Date: Apr 10, 2009
 * Time: 10:58:43 AM
 * >
 */
public class PlayerUpdate implements Packet{
    private static final int END_OF_PLAYERLIST = 2047;
    private static final int UREG_NEEDSHORT = 0x40;
    private static final int UREG_ANIMATION = 0x8;
    private static final int UREG_CHAT = 0x80;
    private static final int UREG_APPEAR = 0x10;
    private static final int UREG_FOCUS = 0x2;
    private static final int UREG_HIT = 0x20;

    public void handleData(RSInputStream instream, Client clientinstance, int psize) throws IOException {
        BitStream b = instream.readBitBlock(psize);
        clientinstance.plpos = 0;
        clientinstance.playerRemoveCount = 0;
        //Start of thisplayerupdate
        thisplayermovementupdate(b,clientinstance);
        otherplayermovementupdate(b,clientinstance);
        addplayers(b,clientinstance,psize);
        processupdateblock(b,clientinstance);
        if (b.pos != psize)
            System.err.println("getplayer - size mismatch , pos : "+b.pos+" psize:"+psize);
    }

    private void processupdateblock(BitStream b,Client c) throws IOException {
           for(int plid = 0;plid < c.plpos;plid++){
               int idx = c.pl[plid];
               Player p = c.playerArray[idx];
               int updatebitreg = b.readByte();
               if ((updatebitreg & UREG_NEEDSHORT) != 0)
                   updatebitreg += b.readByte() << 8;
               op_ublock(b,c,p,idx,updatebitreg);
           }
    }

    private void op_ublock(BitStream b, Client c, Player p, int idx, int i) throws IOException {
        if ((i & UREG_ANIMATION) != 0){
            p.anim = b.readShortLittleEndian();
            p.anim_starttime = System.currentTimeMillis() + b.readByteC();
        }
        if ((i & UREG_CHAT) != 0){
            p.chattype = b.readShortLittleEndian();
            p.rights = b.readByte();
            int clen = b.readByteC();
            byte[] chattext = new byte[clen];
            b.readBytes_reverse(chattext,clen,0);
            c.maingamestate.addMessage(p.name+" says "+ TextUtils.textUnpack(chattext,clen));
        }
        if ((i & UREG_APPEAR) != 0){
            byte[] data = new byte[b.readByteC()];
            b.readBytes(data,data.length,0);
            c.appeareanceStreams[idx] = new RSInputStream(new ByteArrayInputStream(data),data.length);
            p.appeareanceupdate(c.appeareanceStreams[idx]);
        }
        if ((i & UREG_FOCUS) != 0){
            b.pos+=4;//SKIP for now
            //TODO: Implement focus update
        }
        if ((i & UREG_HIT) != 0){
            int hitdifference = b.readByte();
            b.pos+=3;
            c.maingamestate.addMessage(p.name + " took "+hitdifference+" damage");
        }
    }

    private void addplayers(BitStream b,Client c,int size) throws IOException {
        while (b.bitPosition + 10 < size * 8){
            int playeridx = b.readBits(11);
            if (playeridx == END_OF_PLAYERLIST)
                break;
            if (c.playerArray[playeridx] == null){
                c.playerArray[playeridx] = new Player();
                if (c.appeareanceStreams[playeridx] != null)
                    c.playerArray[playeridx].appeareanceupdate(c.appeareanceStreams[playeridx]);
            }
            c.playerIndices[c.playerCount++] = playeridx;
            Player p = c.playerArray[playeridx];
            int needup = b.readBits(1);
            int discardw = b.readBits(1);
            if (needup == 1)
                c.pl[c.plpos++] = playeridx;
            int y = b.readBits(5);
            int x = b.readBits(5);
            if (y > 15)
                y -= 32;
            if (x > 15)
                x -= 32;
            p.setpos(discardw == 1,c.myplayer.x + x,c.myplayer.y + y);
        }
        b.finishBitAccess();
    }

    private void otherplayermovementupdate(BitStream b, Client c) {
        int players = b.readBits(8);
        if (players < c.playerCount){
            for (int pidx = players;pidx < c.playerCount;pidx++)
                c.playersToRemove[c.playerRemoveCount++] = c.playerIndices[pidx];
        }
        if (players > c.playerCount)
        {
            System.err.println("Too many players");
        }
        c.playerCount = 0;
        for (int pidx = 0;pidx < players;pidx++){
            int p = c.playerIndices[pidx];
            Player mp = c.playerArray[p];
            int needupdate = b.readBits(1);
            if (needupdate == 0){
                c.playerIndices[c.playerCount++] = p;
                continue;          
            }
            int updatetype = b.readBits(2);
            if (updatetype == 0){
                c.pl[c.plpos++] = p;//Just a update request
                c.playerIndices[c.playerCount++] = p;
                continue;
            }
            if (updatetype == 1){
                mp.moveindir(b.readBits(3));
                if (b.readBits(1) == 1)
                    c.pl[c.plpos++] = p;
                c.playerIndices[c.playerCount++] = p;
                continue;
            }
            if (updatetype == 2){
                mp.moveindir(b.readBits(3));
                mp.moveindir(b.readBits(3));
                if (b.readBits(1) == 1)
                    c.pl[c.plpos++] = c.myplayerindex;
                c.playerIndices[c.playerCount++] = p;
                continue;
            }
            if (updatetype == 3){
                c.playersToRemove[c.playerRemoveCount++] = p;
                return;
            }
        }
    }

    private void thisplayermovementupdate(BitStream b, Client c) {
        b.initBitAccess();
        int needupdate = b.readBits(1);
        if (needupdate == 0)
            return;
        int updatetype = b.readBits(2);
        if (updatetype == 0){
            c.pl[c.plpos++] = c.myplayerindex;//Just a update request
            return;
        }
        if (updatetype == 1){
            c.myplayer.moveindir(b.readBits(3));
            if (b.readBits(1) == 1)
                c.pl[c.plpos++] = c.myplayerindex;
            return;
        }
        if (updatetype == 2){
            c.myplayer.moveindir(b.readBits(3));
            c.myplayer.moveindir(b.readBits(3));
            if (b.readBits(1) == 1)
                c.pl[c.plpos++] = c.myplayerindex;
            return;
        }
        if (updatetype == 3){
            c.plane = b.readBits(2);
            int discardw = b.readBits(1);
            int needup = b.readBits(1);
            int y = b.readBits(7);
            int x = b.readBits(7);
            c.positionCam(MAP_OFF+x, MAP_OFF+y,c.plane,1);
            c.myplayer.setpos(discardw == 1,x,y);
            if (needup == 1)
                c.pl[c.plpos++] = c.myplayerindex;

        }
    }

}
